﻿using System;
using System.Linq.Expressions;

namespace CSharpExecutor
{
	/// <summary>
	/// Used to make assertions about the input variables.
	/// </summary>
	public static class Require
	{
		/// <summary>
		/// Creates a new argument asserter.
		/// </summary>
		/// <typeparam name="T">The type of the variable.</typeparam>
		/// <param name="name">The name of the variables.</param>
		/// <param name="value">The value of the variable.</param>
		/// <returns>A variable asserter.  See the RequireVariableExtensions class for more information.</returns>
		public static RequireVariable<T> Variable<T>(string name, T value)
		{
			return new RequireVariable<T>(name, value);
		}

		/// <summary>
		/// Creates a new argument asserter.
		/// </summary>
		/// <remarks>
		/// The input expression must be a "member expression", i.e. a lambda expression that only
		/// accesses a variable/property (() => arg).  The Expression input is case as a MemberExpression
		/// to get the name of the variable being tested, then the expression is compiled and invoked
		/// to get the value of the variable.  The name and value are then used to construct the
		/// RequireVariable object.
		/// </remarks>
		/// <typeparam name="T">The type of the variable.</typeparam>
		/// <param name="arg">An expression which accesses the variable being tested.</param>
		/// <returns>A variable asserter.  See the RequireVariableExtensions class for more information.</returns>
		public static RequireVariable<T> Variable<T>(Expression<Func<T>> arg)
		{
			var exprBody = arg.Body as MemberExpression;
			if (exprBody == null)
			{
				throw new ArgumentException("Input expression must be a member accessor.", "arg");
			}

			var name = exprBody.Member.Name;
			var value = arg.Compile()();

			return new RequireVariable<T>(name, value);
		}

		/// <summary>
		/// The variable asserter.  See the RequireVariableExtensions class for more information.
		/// </summary>
		/// <typeparam name="T">The type of the variable being tested.</typeparam>
		public class RequireVariable<T>
		{
			#region Constructors

			/// <summary>
			/// Initializes a new instance of the <see cref="RequireVariable&lt;T&gt;"/> class.
			/// </summary>
			/// <param name="name">The name of the variable.</param>
			/// <param name="value">The value of the variable.</param>
			internal RequireVariable(string name, T value)
			{
				if (string.IsNullOrEmpty(name))
				{
					throw new ArgumentException("Cannot be null or empty.", "name");
				}

				Name = name;
				Value = value;
			}

			#endregion

			#region Properties

			/// <summary>
			/// Get the name of the variable being tested.
			/// </summary>
			public string Name { get; private set; }

			/// <summary>
			/// Get the value of the variable being tested.
			/// </summary>
			public T Value { get; private set; }

			#endregion
		}
	}
}